﻿/**
*    Copyright (C) 2008  Chase Kernan 
*    chase.kernan@gmail.com
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
* 
**/

package com.chasekernan.hxnova.core.players;

import com.chasekernan.hxnova.core.dataholders.Global;
import com.chasekernan.hxnova.core.dataholders.Players;
import com.chasekernan.hxnova.utils.XmlBuilder;

/**
    A player can be either a friend, a nuetral, or an enemy of another player.
**/
enum Relation {
    Friend;
    Nuetral;
    Enemy;
}


/**
    Contains the relations of one player to everyone else.
**/
class PlayerRelations {
    
    public var parent : Player;
    public var relations : Array < Relation >;
    
    /**
        Sets each player defined in [Global.players] to having a nuetral
        relation with the given player.
    **/
    public function new(p : Player) {
        parent = p;
        
        relations = new Array();
        
        for (player in Global.players) {
            relations.push(if (parent.id == player.id) null else Nuetral);
        }
    }
    
    /**
        Returns true if the given player is an enemy.
        Will throw an error if the given player is null or the [nullPlayer] is
        given.
    **/
    public function isEnemyOf(p : Player) : Bool {
        if (p == null || p.id == Global.players.nullPlayer.id) {
            throw "The given player cannot be null or the [nullPlayer].";
        }
        
        return switch(relations[p.id]) {
            case Enemy:
                true;
            default:
                false;
        };  
    }
    
    /**
        Returns true if the given player is a friend.
        Will throw an error if the given player is null or the [nullPlayer] is
        given.
    **/
    public function isFriendOf(p : Player) : Bool {
        if (p == null || p.id == Global.players.nullPlayer.id) {
            throw "The given player cannot be null or the [nullPlayer].";
        }
        
        return switch(relations[p.id]) {
            case Friend:
                true;
            default:
                false;
        };
    }
    
    /**
        Returns true if the [parent] is nuetral to the given player.
        Will throw an error if the given player is null or the [nullPlayer] is
        given.
    **/
    public function isNuetralTo(p : Player) : Bool {
        if (p == null || p.id == Global.players.nullPlayer.id) {
            throw "The given player cannot be null or the [nullPlayer].";
        }
        
        return switch(relations[p.id]) {
            case Nuetral:
                true;
            default:
                false;
        };
    }
    
    /**
        Returns the relation of the [parent] to the given player.
        Will throw an error if the given player is null, the [nullPlayer] is
        given, or the given player is the same as the [parent].
    **/
    public function getRelationTo(p : Player) : Relation {
        if (p == null || p.id == Global.players.nullPlayer.id) {
            throw "The given player cannot be null or the [nullPlayer].";
        } else if (p.id == parent.id) {
            throw "Cannot give the same player as [parent].";
        }
        
        return relations[p.id];
    }
    
    /**
        Sets the relation towards the given player.
        Will throw an error if the given player is null, the [nullPlayer] is
        given, the given player is the same as the [parent], or [r] == null.
    **/
    public function setRelationTo(p : Player, r : Relation) {
        if (p == null || p.id == Global.players.nullPlayer.id) {
            throw "The given player cannot be null or the [nullPlayer].";
        } else if (p.id == parent.id) {
            throw "Cannot give the same player as [parent].";
        } else if (r == null) {
            throw "Relation cannot be null.";
        }
        
        relations[p.id] = r;
    }
    
    public static var XML_TITLE = "relations";
    
    /**
        Writes the relations of a given player to xml.
        Sample xml: 
        <relations parent="3">
            <relation player="0">friend</relation><spacer/>
            <relation player="1">nuetral</relation><spacer/>
            <relation player="2">enemy</relation><spacer/>
            <relation player="4">enemy</relation><spacer/>
            ...
        </relations>
    **/
    public static function writeToXml(pr : PlayerRelations) : Xml {
        var xml = new XmlBuilder(XML_TITLE);
        
        xml.addAttribute("parent", pr.parent.id);
        
        for (i in 0...pr.relations.length) {
            if (pr.relations[i] == null) continue;
            
            var rXml = new XmlBuilder("relation");
            rXml.addAttribute("player", i);
            
            rXml.xml.firstChild().nodeValue = switch(pr.relations[i]) {
                
                case Friend: "friend";
                
                case Nuetral: "nuetral";
                
                case Enemy: "enemy";
            }
            
            xml.addChild(rXml);
        }
        
        return xml.xml;
    }
    
    /**
        Reads the relations from an xml file.
        Will throw an error if there is a mistake.
    **/
    public static function readFromXml(xml : Xml) : PlayerRelations {
        try {
            var xb = XmlBuilder.fromXml(xml);
            
            var parent = Global.players.getPlayerById(
                    xb.readIntAttribute("parent"));
                    
            var pr = new PlayerRelations(parent);
                    
            for (rXml in xb.xml.elementsNamed("relation")) {
                var rXb = XmlBuilder.fromXml(rXml);
                var player = Global.players.getPlayerById(
                    rXb.readIntAttribute("player"));
                    
                pr.setRelationTo(player, 
                        switch(rXb.xml.firstChild().nodeValue) {
                    
                    case "friend": Friend;
                    
                    case "nuetral": Nuetral;
                    
                    case "enemy": Enemy;
                });
            }
            
            return pr;
        } catch (e : Dynamic) {
            throw "Error while reading player relations: " + e;
        }
    }
}